#include <ctype.h>
#include <glib.h>
#include <gtk/gtk.h>
#include "results.h"
#include "glib_port.h"

#define GETSTRING(x) (fscanf(readfile,"%s",x))
#define GETLINE(x) (fscanf(readfile,"%255[^\n]",x))
#define FSCANLINE(x) fscanf(readfile,"%1023[^\n]\n",x)
#define GETCURDIR (strcmp(dirName,"") ? dirName : ".")

extern void get_queryfilsectotals_mem();
extern void create_ctgtotals_array();
extern void get_parmlist_mem();
extern void rewriteFile();
extern int sortHits1();
extern int sortHits2();
extern int sortSeqCtg();
extern int sortMark();
extern int sortScore();
extern void make_fil_ref();
extern void add_marker();
extern void add_fpremark();
extern void refreshlist();
extern void updateproj();
extern void sortoutmarkersman();
extern void markersinctgcount();
extern void sortoutmarkerindex(void);
extern void settime(struct mytime *mytime);
extern void update_contigs_list(int marker_index);
extern void bss_check_contigs(BSS_INST* pbsi);

extern char qryDir[];
extern char blastoutfile[];
extern long double eVal;
extern int blatscore;
extern struct displayedFile *displayed_files;
extern int num_displayed_files;
int num_queries(BSS_INST* pbsi);
void free_ctg_array();
void p_cmd_callback(GtkWidget *widget, GtkWidget *entry);
void close_print(GtkWidget* _w, gpointer _p);
void display_blat_alignment(int entrynum);
void display_blat_block(int _qstart, int _tstart, int size, char* qblock, char* tblock, int rev);
static void filter_hits(GtkWidget* _w, gpointer _p);
void addfpremark(char *junk,int index);
void addremark(char *junk,int index);

int bss_ncols;
struct bss_instance g_bsi;

int g_sort_factor = 1;
BSS_TYPE g_sort_col;


char g_oneline[1024];

GtkWidget* g_string_menu, *g_numeric_menu;
GtkWidget* g_string_filter, *g_numeric_filter;
GtkWidget* g_minctghits_filter, *g_maxctgshit_filter, *g_maxhits_filter,*g_fromend_filter;
GtkWidget *g_numeric_radio, *g_string_radio, *g_maxctgshit_radio,
        *g_maxhits_radio,*g_minctghits_radio,*g_ctgends_radio;
GtkWidget* g_string_options;
GtkWidget* g_numeric_options;
GtkCList* fpc_history_list;
GtkWidget* filter_window = 0;
GtkWidget* g_cmp_menu;
GtkWidget* add_hits_window = 0;
GtkWidget* prefix_entry_box;
GtkWidget* add_mrk_radio;
GtkWidget* add_rem_radio;
GtkWidget* add_fprem_radio;
int g_cmp = -1;

int g_filter_field;


void refresh_query_table(){
   char tempstr[MAXCOLSIZE];
   int i;
   QUERY_HIT_DATA* pqd;
   gchar* fields[5];

   gtk_clist_freeze(GTK_CLIST(r_clist1));
   gtk_clist_clear(GTK_CLIST(r_clist1));
   for(i=0;i < arrayMax(g_bsi.bss_queryhits);i++)
   {

      pqd = arrp(g_bsi.bss_queryhits,i,QUERY_HIT_DATA);
      if (pqd->hits == 0) continue;

      fields[0] = g_strdup(pqd->query);

      sprintf(tempstr,"%d/%d",pqd->hits,pqd->numCtgsHit);
      fields[1]= g_strdup(tempstr);

      sprintf(tempstr, "%d/%d", pqd->bestCtg, pqd->hitsBestCtg);

      fields[2]= g_strdup(tempstr);
      gtk_clist_append((GtkCList *) r_clist1,fields);
   }

   gtk_clist_thaw(GTK_CLIST(r_clist1));
}

/***************************************************************
		refresh_contig_table()
		Friedrich Engler

Rewrites the contents of the contig table in the results window
***************************************************************/
void refresh_contig_table()
{
    gchar *temp[1][3];
    char tempstr[3][MAXCOLSIZE];
    int i;
    CTG_HIT_DATA* pcd;

    gtk_clist_freeze(GTK_CLIST(r_clist2));
    gtk_clist_clear(GTK_CLIST(r_clist2));
    for (i = 0; i < arrayMax(g_bsi.bss_ctghits); i++) 
    {
        pcd = arrp(g_bsi.bss_ctghits,i,CTG_HIT_DATA);
        if (pcd->cloneHits == 0) continue;

        sprintf(tempstr[0], "%d", pcd->contig);
        temp[0][0] = tempstr[0];
        //sprintf(tempstr[1], "%d", pcd->cst);
        //temp[0][1] = tempstr[1];
        sprintf(tempstr[2], "%d", pcd->cloneHits);
        temp[0][1] = tempstr[2];
        gtk_clist_append(GTK_CLIST(r_clist2), temp[0]);
        if (0) //split_contig == w->contig_table[i].contig) 
        {
            GdkColor bg;
            bg.red = 0xFF00;
            bg.green = 0xFF00;
            bg.blue = 0x9900;
            if (gdk_colormap_alloc_color(gdk_colormap_get_system(),
                                         &bg, FALSE, TRUE))
                gtk_clist_set_background(GTK_CLIST(r_clist2), i, &bg);
        }
    }
    gtk_clist_thaw(GTK_CLIST(r_clist2));
}

int count_unfiltered()
{
    int i;
    int ret = 0;
    BSS_HIT* pbh;

    for (i = 0; i < arrayMax(g_bsi.bss_hits); i++)
    {
        pbh = arrp(g_bsi.bss_hits,i,BSS_HIT);
        if (!pbh->filtered)
        {
            ret++;
        }
    }
    return ret;
}



void refresh_hitlist(){

    int i,j;

    gchar* fields[BSS_NUMTYPES];
    char field[BSS_FIELD_MAXSIZE+1];
    BSS_HIT* pbh;

    gtk_clist_freeze(GTK_CLIST(r_clist3));
    gtk_clist_clear(GTK_CLIST(r_clist3));

    for (i = 0; i < arrayMax(g_bsi.bss_hits); i++)
    {
        pbh = arrp(g_bsi.bss_hits,i,BSS_HIT);
        
        if (pbh->filtered) continue;

        for (j = 0; j < bss_ncols - BSS_HIDDEN_FIELDS; j++)
        {
            bss_format_field(field,pbh,bss_column_order[j]);
            fields[j] = g_strdup(field);
        }
        gtk_clist_append((GtkCList *) r_clist3,fields);
    }

    gtk_clist_thaw(GTK_CLIST(r_clist3));

}


/*+++++++++++++++++++++*******************************************
 
        Hit List sorting

*********************************************++++++++++++++++++++*/

int bss_sort_integer(void* p1, void* p2)
{
    BSS_HIT* pb1 = (BSS_HIT*)p1;    
    BSS_HIT* pb2 = (BSS_HIT*)p2;

    assert(pb1);assert(pb2);

    if (pb1->data[g_sort_col].i > pb2->data[g_sort_col].i) return g_sort_factor;
    else if (pb1->data[g_sort_col].i < pb2->data[g_sort_col].i) return -g_sort_factor;
    else if (pb1->order > pb2->order) return g_sort_factor;
    else if (pb1->order < pb2->order) return -g_sort_factor;
    else assert(0);  
}
int bss_sort_string(void* p1, void* p2)
{
    int ret;

    BSS_HIT* pb1 = (BSS_HIT*)p1;    
    BSS_HIT* pb2 = (BSS_HIT*)p2;

    assert(pb1);assert(pb2);

    ret = g_sort_factor*strcmp(pb1->data[g_sort_col].str,pb2->data[g_sort_col].str);
    if (ret == 0)
    {
        ret = (pb1->order > pb2->order ? g_sort_factor : -g_sort_factor);
    }

    return ret;
}
int bss_sort_evalue(void* p1, void* p2)
{
    int ret;
    double e1, e2;

    BSS_HIT* pb1 = (BSS_HIT*)p1;    
    BSS_HIT* pb2 = (BSS_HIT*)p2;

    assert(pb1);assert(pb2);

    e1 = strtod(pb1->data[g_sort_col].str,0);
    e2 = strtod(pb2->data[g_sort_col].str,0);

    ret = (e1 > e2 ? g_sort_factor : -g_sort_factor);

    if (ret == 0)
    {
        ret = (pb1->order > pb2->order ? g_sort_factor : -g_sort_factor);
    }

    return ret;
}
void bss_sort_hitlist(BSS_TYPE field,int factor, struct bss_instance* pbsi)
{
    int (*sort_func)();
    int i;

    g_sort_factor = factor;
    g_sort_col = field;

    switch (field)
    {
        case BSS_CTGLR:
            g_sort_col = BSS_CTG;           
        case BSS_QLEN:
        case BSS_QSTART:
        case BSS_QEND:
        case BSS_TSTART:
        case BSS_TEND:
        case BSS_TLEN:
        case BSS_SEQCTG:
        case BSS_CTG:
        case BSS_SCORE:
        case BSS_HITLEN:
        case BSS_IDENTITY:
        case BSS_MATCH:
        case BSS_INTRON:
            sort_func = bss_sort_integer;
            break;
        case BSS_SEQ:
        case BSS_BES:
        case BSS_MARKER:
        case BSS_RC:
        case BSS_CLONE:
        //case BSS_QUERY:
        case BSS_TARGET:
        case BSS_LR:
            sort_func = bss_sort_string;
            break;
        case BSS_EVALUE:
            sort_func = bss_sort_evalue;
            break;
        default:
            //assert(0);
        break;
    } 
    qsort((void*)arrp(pbsi->bss_hits,0,BSS_HIT),arrayMax(pbsi->bss_hits),sizeof(BSS_HIT),sort_func);
    for (i = 0; i < arrayMax(pbsi->bss_hits); i++)
    {
        arrp(pbsi->bss_hits,i,BSS_HIT)->order = i;
    }
}

void hitlist_column_click(GtkWidget *widget ,GdkEventButton *event,gpointer p)
{   
    BSS_TYPE coltype = *(BSS_TYPE*)p;

    if(event->type!=GDK_BUTTON_PRESS) return;   

   if(event->state & GDK_SHIFT_MASK)
      bss_sort_hitlist(coltype,-1,&g_bsi);
   else
      bss_sort_hitlist(coltype,1,&g_bsi);

    refresh_hitlist();
}


/*+++++++++++++++++++++***********************************************

             Query List sorting 

***********************************************++++++++++++++++++++*/

int bss_querysec_sort_query(void* _p1, void* _p2)
{   
    int ret;

    QUERY_HIT_DATA* p1 = (QUERY_HIT_DATA*)_p1;    
    QUERY_HIT_DATA* p2 = (QUERY_HIT_DATA*)_p2;
    assert(p1);assert(p2);assert(p1->query);assert(p2->query);
    
    ret = g_sort_factor*strcmp(p1->query,p2->query);
    if (ret == 0)
    {
        ret = (p1->order > p2->order ? g_sort_factor : -g_sort_factor);
    }
    return ret;
}

int bss_querysec_sort_len(void* _p1, void* _p2)
{   
    int ret;

    QUERY_HIT_DATA* p1 = (QUERY_HIT_DATA*)_p1;    
    QUERY_HIT_DATA* p2 = (QUERY_HIT_DATA*)_p2;

    assert(p1);assert(p2);    
    ret = (p1->size > p2->size ? g_sort_factor : -g_sort_factor);
    if (ret == 0)
    {
        ret = (p1->order > p2->order ? g_sort_factor : -g_sort_factor);
    }
    return ret;
}

int bss_querysec_sort_hits(void* _p1, void* _p2)
{   
    int ret;

    QUERY_HIT_DATA* p1 = (QUERY_HIT_DATA*)_p1;    
    QUERY_HIT_DATA* p2 = (QUERY_HIT_DATA*)_p2;

    assert(p1);assert(p2);    
    ret = (p1->hits > p2->hits ? g_sort_factor : -g_sort_factor);
    if (ret == 0)
    {
        ret = (p1->order > p2->order ? g_sort_factor : -g_sort_factor);
    }
    return ret;
}

int bss_querysec_sort_bestctg(void* _p1, void* _p2)
{   
    int ret;

    QUERY_HIT_DATA* p1 = (QUERY_HIT_DATA*)_p1;    
    QUERY_HIT_DATA* p2 = (QUERY_HIT_DATA*)_p2;

    assert(p1);assert(p2);    
    ret = (p1->bestCtg > p2->bestCtg ? g_sort_factor : -g_sort_factor);
    if (ret == 0)
    {
        ret = (p1->order > p2->order ? g_sort_factor : -g_sort_factor);
    }
    return ret;
}

int bss_querysec_sort_seqctg(void* _p1, void* _p2)
{   
    int ret;

    QUERY_HIT_DATA* p1 = (QUERY_HIT_DATA*)_p1;    
    QUERY_HIT_DATA* p2 = (QUERY_HIT_DATA*)_p2;

    assert(p1);assert(p2);    
    ret = (p1->seqCtg > p2->seqCtg ? g_sort_factor : -g_sort_factor);
    if (ret == 0)
    {
        ret = (p1->order > p2->order ? g_sort_factor : -g_sort_factor);
    }
    return ret;
}

void bss_sort_querysec(int col, int factor, struct bss_instance* pbsi)
{
    int (*sort_func)();
    int i;
    g_sort_factor = factor;
    switch(col)
    {
        case 1: // query name
            sort_func = bss_querysec_sort_query;
            break;
        case 2: // query length
            sort_func = bss_querysec_sort_len;
            break;
        case 3: // # hits
            sort_func = bss_querysec_sort_hits;
            break;
        case 4: // # best ctg
            sort_func = bss_querysec_sort_bestctg;
            break;
        case 5: // # seqctg
            sort_func = bss_querysec_sort_seqctg;
            break;
        default:
            assert(0);
    }

    qsort((void*)arrp(pbsi->bss_queryhits,0,QUERY_HIT_DATA),arrayMax(pbsi->bss_queryhits),sizeof(QUERY_HIT_DATA),sort_func);
    for (i = 0; i < arrayMax(pbsi->bss_queryhits); i++)
    {
        arrp(pbsi->bss_queryhits,i,QUERY_HIT_DATA)->order = i;
    }
}

void querylist_column_click(GtkWidget *widget ,GdkEventButton *event,gpointer p)
{
    int col = (int)p;
    int factor = (event->state & GDK_SHIFT_MASK ? -1 : 1);
    
    if(event->type!=GDK_BUTTON_PRESS) return; 
  
    bss_sort_querysec(col,factor,&g_bsi);
    refresh_query_table();
}

/*+++++++++++++++++++++*************************************************

                 Contig List sorting 

**************************************************++++++++++++++++++++*/

int bss_ctgsec_sort_ctg(void* _p1, void* _p2)
{   
    int ret;

    CTG_HIT_DATA* p1 = (CTG_HIT_DATA*)_p1;    
    CTG_HIT_DATA* p2 = (CTG_HIT_DATA*)_p2;

    assert(p1);assert(p2);    
    ret = (p1->contig > p2->contig ? g_sort_factor : -g_sort_factor);
    if (ret == 0)
    {
        ret = (p1->order > p2->order ? g_sort_factor : -g_sort_factor);
    }
    return ret;
}
int bss_ctgsec_sort_cst(void* _p1, void* _p2)
{   
    int ret;

    CTG_HIT_DATA* p1 = (CTG_HIT_DATA*)_p1;    
    CTG_HIT_DATA* p2 = (CTG_HIT_DATA*)_p2;

    assert(p1);assert(p2);    
    ret = (p1->cst > p2->cst ? g_sort_factor : -g_sort_factor);
    if (ret == 0)
    {
        ret = (p1->order > p2->order ? g_sort_factor : -g_sort_factor);
    }
    return ret;
}
int bss_ctgsec_sort_hits(void* _p1, void* _p2)
{   
    int ret;

    CTG_HIT_DATA* p1 = (CTG_HIT_DATA*)_p1;    
    CTG_HIT_DATA* p2 = (CTG_HIT_DATA*)_p2;

    assert(p1);assert(p2);    
    ret = (p1->cloneHits > p2->cloneHits ? g_sort_factor : -g_sort_factor);
    if (ret == 0)
    {
        ret = (p1->order > p2->order ? g_sort_factor : -g_sort_factor);
    }
    return ret;
}
void bss_sort_ctgsec(int col, int factor, struct bss_instance* pbsi)
{
    int (*sort_func)();
    int i;

    g_sort_factor = factor;
    switch(col)
    {
        case 1: // ctg
            sort_func = bss_ctgsec_sort_ctg;
            break;
        case 2: // cst
            sort_func = bss_ctgsec_sort_cst;
            break;
        case 3: // # hits
            sort_func = bss_ctgsec_sort_hits;
            break;
        default:
            assert(0);
    }

    qsort((void*)arrp(pbsi->bss_ctghits,0,CTG_HIT_DATA),arrayMax(pbsi->bss_ctghits),sizeof(CTG_HIT_DATA),sort_func);
    for (i = 0; i < arrayMax(pbsi->bss_ctghits); i++)
    {
        arrp(pbsi->bss_ctghits,i,CTG_HIT_DATA)->order = i;
    }
}

void ctglist_column_click(GtkWidget *widget ,GdkEventButton *event,gpointer p)
{
    int col = (int)p;
    int factor = (event->state & GDK_SHIFT_MASK ? -1 : 1);
    
    if(event->type!=GDK_BUTTON_PRESS) return; 
  
    bss_sort_ctgsec(col,factor,&g_bsi);
    refresh_contig_table();
}


/*+++++++++++++++++++End sorting callbacks++++++++++++++++++*/


void save_results(GtkWidget* _w, gpointer _p)
{
    int changed = 0;
    int i;
    gchar* m;
    int messres;

    for (i = 0; i < arrayMax(g_bsi.bss_hits); i++)
    {
        if (arrp(g_bsi.bss_hits,i,BSS_HIT)->filtered > 0)
        {
            changed = 1;
            break;
        }
    }
    
    if (changed)
    {
        m = g_strdup_printf("You have filtered the BSS results. If you save the file, the removed data can not be restored. Continue?");
        messres = messQuery(m);
        g_free(m);
        if (messres == 0) return;
    }
    write_bss_file(&g_bsi,1);
}

void saveas_results(GtkWidget* _w, gpointer _p)
{
    FILE *writefile;
    char newfilename[FIL_BUFFER_SIZE];
    char newdirname[DIR_BUFFER_SIZE];
    char newfile[DIR_BUFFER_SIZE+FIL_BUFFER_SIZE];
    char end[100];
    char* ptr;

    strcpy(end,"bss");

    if (rindex(g_bsi.bssfile,'/')) 
    {
        sprintf(newfilename,"%s",rindex(g_bsi.bssfile,'/')+1);
        ptr = g_bsi.bssfile;
        if (strlen(ptr) > 2 && !strncmp("./",ptr,2))
        {
            ptr += 2;
        }
        strcpy(newdirname,ptr);
        *rindex(newdirname,'/') = 0;
    }
    else 
    {
        sprintf(newfilename,"%s",g_bsi.bssfile);
        sprintf(newdirname,"%s/%s",GETCURDIR,"BSS_results");
    }
    suffixremove(newfilename);
    writefile=graphQueryOpen(newdirname,newfilename,end,"w","Enter new BSS file name");
    if(writefile!=NULL)
    {
        sprintf(newfile,"%s/%s.%s",newdirname,newfilename,end);
        fprintf(stdout,"Writing file %s...",newfile);
        fflush(stdout);
        fclose(writefile);
        strcpy(g_bsi.bssfile,newfile);
        write_bss_file(&g_bsi,0);
        printf("done\n");fflush(0);
   }

}

void output_column_headers_csv(FILE* f)
{
    char line[1000];
    char fmt[100];
    char field[100];
    int j;

    line[0] = 0;
    for (j = 0; j < bss_ncols - BSS_HIDDEN_FIELDS; j++)
    {
        if (!bss_columns_checked[j]) continue;
        sprintf(fmt,"%%-%ds ",bss_col_widths[bss_column_order[j]]);
        sprintf(field,fmt,bss_headers[bss_column_order[j]]);  
        strcat(line,field);
    }
    strcat(line,"\n");
    fputs(line,f);
}

void save_as_csv(GtkWidget* _w, gpointer _p)
{   
    int i,j;
    BSS_HIT* hit;
    char line[1000];
    char field[100];
    FILE* f;
    char newfilename[MAXPATHLEN];
    char newdirname[MAXPATHLEN];
    char end[MAXPATHLEN];
    char outfile[MAXPATHLEN]; 
   
    strcpy(newdirname,".");
    strcpy(newfilename,"bss_hits.txt");
    end[0] = 0;

    f = graphQueryOpen(newdirname,newfilename,end,"w","Enter output file name");
    if (f == NULL) return;

    sprintf(outfile,"%s/%s.%s",newdirname,newfilename,end);
    printf("Writing file %s...",outfile);fflush(0);

    output_column_headers_csv(f);
    for (i = 0; i < arrayMax(g_bsi.bss_hits); i++)
    {
        line[0] = 0;
        hit = arrp(g_bsi.bss_hits,i,BSS_HIT);
        for (j = 0; j < bss_ncols - BSS_HIDDEN_FIELDS; j++)
        {
            if (bss_columns_checked[j])
            {
                bss_format_field(field,hit,bss_column_order[j]);  
                strcat(line,field);
            }
        }
        strcat(line,"\n");
        fputs(line,f);
    }
    printf("done\n");fflush(0);
    fclose(f);
}




void r_display_contig(GtkWidget *clist,
                      gint row,
                      gint column,
                      GdkEventButton *event,
                      gpointer reserved)
{
    gchar *text;
    int ctg;

    gtk_clist_get_text(GTK_CLIST(clist), row, 0, &text);
    ctg = atoi(text);

    if (ctg == 0) return;

    if (ctg != currentctg) {
        currentclone = -1;
        ctgdisplay(ctg);

    }

    if (ctg_window != NULL)
        gdk_window_raise(ctg_window->window);

}

int extract_sequence(char* name,char* file,char* dir,char* outfile, search_tool_types program)
{
    FILE* f, *outfp;
    char line[1024];
    char* ptr;
    char inpath[MAXPATHLEN];
    int found = 0;
    int gb_headers = 0;

    sprintf(inpath,"%s/%s",dir,file);

    f = fopen(inpath,"r");
    if (f)
    {
        while (!found && fgets(line,1024,f))
        {
            if (line[0] == '>')
            {
                if (0 == strncmp(line,">gi|",4))
                {
                    gb_headers = 1;
                }
                /* parse to the first space */
                ptr = &line[1];
                while (isspace(*ptr)) ptr++;
                while (*ptr && !isspace(*ptr) && *ptr != '\n')
                {
                    ptr++;
                } 
                *ptr = 0;
                ptr = &line[1];                
                while (isspace(*ptr)) ptr++;
                if (  (gb_headers == 0 && !strcmp(ptr,name)) || 
                        (gb_headers == 1 && 0 != strstr(ptr,name))  )
                {
                    strcat(ptr,"\n");
                    outfp = fopen(outfile,"w");
                    fprintf(outfp,"%s",line);
                    found = 1;
                    while (fgets(line,1024,f))
                    {
                        if (line[0] == '>') break;
                        fprintf(outfp,"%s",line);
                    }           
                    fclose(outfp);
                }
            }
        }
        fclose(f);
    }
    return found;
}
void r_selection_made(GtkWidget *clist,
                      gint row,
                      gint column,
                      GdkEventButton *event,gpointer _p)
{
    BSS_HIT* h;
	
    char qseq[MAXPATHLEN];
    char tseq[MAXPATHLEN];
    char blatout[MAXPATHLEN];
    char cmd_str[MAXPATHLEN];
    int found, i;
    char *tseqname;
	int hitnum, hitcount;

	/* We have to account for filtering !! */
	hitcount = 0;
	for (hitnum = 0; hitnum < arrayMax(g_bsi.bss_hits) ; hitnum++)
	{		
    	h = arrp(g_bsi.bss_hits,hitnum,BSS_HIT);
		if (h->filtered == 0)
		{
			hitcount++;
		}
		if (hitcount == row+1)
		{
			break; 
		}
	}
	if (hitcount != row+1)
	{
		printf("Unable to show detailed hit!\n");
		return;
	}
    printf("%s %s\n",h->data[BSS_MARKER].str,h->data[BSS_BES].str);fflush(0);

    tmpnam(qseq);
    tmpnam(tseq);

    // locate the query and target in one of the databases

    tseqname = h->data[BSS_BES].str ;

    if (!extract_sequence(h->data[BSS_MARKER].str,g_bsi.query_file,g_bsi.query_dir,qseq,g_bsi.program))
    {
        printf("Unable to find %s in %s/%s\n",h->data[BSS_MARKER].str,g_bsi.query_dir,g_bsi.query_file);fflush(0);
        return;
    }  

    found = 0;
    for (i = 0; i < g_bsi.dbs->len; i++)
    {
        printf("Look for %s in %s/%s....",tseqname,g_bsi.db_dir,g_array_index(g_bsi.dbs,char*,i));fflush(0);
        if (extract_sequence(tseqname,g_array_index(g_bsi.dbs,char*,i),g_bsi.db_dir,tseq,g_bsi.program))
        {
            printf("found\n");fflush(0);
            found = 1;
            break;
        }      
        else
        {
            printf("not found\n");fflush(0);
        }    
    }

    if (!found)
    {
        printf("Unable to find %s in database files\n",h->data[BSS_TARGET].str);fflush(0);
        return;
    }  
  

    if (g_bsi.program == BLAT)
    {
        tmpnam(blatout);
        sprintf(cmd_str, "blat %s %s -minScore=%d -out=blast %s %s",
                     tseq,qseq, g_bsi.blat_score, g_bsi.xtraParm,blatout);
        system(cmd_str);       
        sprintf(cmd_str,"cat %s",blatout);
        system(cmd_str);       
        sprintf(cmd_str,"rm %s",blatout);
        system(cmd_str);       
    }
    else if (g_bsi.program == BLAST)
    {
        sprintf(cmd_str,"formatdb -i %s -p F -o T",tseq);
        fpc_exec(cmd_str);
        sprintf(cmd_str, "blastall -p blastn -d %s -i %s -e %s %s",
                     tseq,qseq, g_bsi.cutoff_str, g_bsi.xtraParm);
        fpc_exec(cmd_str);
        sprintf(cmd_str,"rm %s.nhr",tseq);
        system(cmd_str);
        sprintf(cmd_str,"rm %s.nin",tseq);
        system(cmd_str);
        sprintf(cmd_str,"rm %s.nsd",tseq);
        system(cmd_str);
        sprintf(cmd_str,"rm %s.nsi",tseq);
        system(cmd_str);
        sprintf(cmd_str,"rm %s.nsq",tseq);
        system(cmd_str);
    }
    else if (g_bsi.program == MEGABLAST)
    {
        sprintf(cmd_str,"formatdb -i %s -p F -o T",tseq);
        fpc_exec(cmd_str);
        sprintf(cmd_str, "megablast -d %s -i %s -e %s %s",
                     tseq,qseq, g_bsi.cutoff_str, g_bsi.xtraParm);
        fpc_exec(cmd_str);
        sprintf(cmd_str,"rm %s.nhr",tseq);
        system(cmd_str);
        sprintf(cmd_str,"rm %s.nin",tseq);
        system(cmd_str);
        sprintf(cmd_str,"rm %s.nsd",tseq);
        system(cmd_str);
        sprintf(cmd_str,"rm %s.nsi",tseq);
        system(cmd_str);
        sprintf(cmd_str,"rm %s.nsq",tseq);
        system(cmd_str);
    }
    
    sprintf(cmd_str,"rm %s",qseq);
    system(cmd_str);
    sprintf(cmd_str,"rm %s",tseq);
    system(cmd_str);


    return;
}



void view_hit_clone_keyset(GtkWidget* _w, gpointer _p)
{
    int i,j;
    BSS_HIT* hit;
    struct list* p;
    GHashTable* htbl;
    int n = 0;

    freelistmem();

    htbl = g_hash_table_new(g_str_hash,g_str_equal);
    for (i = 0; i < arrayMax(g_bsi.bss_hits); i++) 
    {
        hit = arrp(g_bsi.bss_hits,i,BSS_HIT);
        if (hit->filtered) continue;
        if (!fppFind(acedata,hit->data[BSS_CLONE].str,&j,(void *) cloneOrder)) continue;  
        if (g_hash_table_lookup(htbl,hit->data[BSS_CLONE].str)) continue;
        g_hash_table_insert(htbl,hit->data[BSS_CLONE].str,(void*)1);
        n++;

        if (!listroot)
        {
            listroot = (struct list *)messalloc(sizeof(struct list));
            p = listroot;
            p->next = NULL;
        }
        else
        {
            p->next = (struct list *)messalloc(sizeof(struct list));
            p = p->next;
            p->next = NULL;
        }
        p->index = j;
  
    }

    if (classctg != CLONECLASS) setclassclone();
    displaykeyset(n);
    graphPop();
}
void close_add_hits_window(GtkWidget *widget, gpointer data)
{
    gtk_widget_destroy((GtkWidget*)add_hits_window);
    add_hits_window = 0;
}
/*
 * remarkType == 0 -> Electronic
 * remarkType == 1 -> eConfirmed */
static void
add_e_add_remark(int cloneindex, char *marker, int remarkType){
  char remarkStr[COMMENT_SZ+1];
  char remarkStr2[COMMENT_SZ+1];
  CLONE *clp;
  struct remark *rptr;

  sprintf(remarkStr, "Electronic (%s)", marker);
  sprintf(remarkStr2, "eConfirmed (%s)", marker);
  clp=arrp(acedata, cloneindex, CLONE);
  if(clp->remark==NULL){
    clp->remark=malloc(sizeof(struct remark));
    rptr=clp->remark;
  }
  else{
    for(rptr=clp->remark; rptr->next!=NULL; rptr=rptr->next){
      if(!strcmp(rptr->message, remarkStr) ||
	 !strcmp(rptr->message, remarkStr2)){
	/* Don't add remark twice*/
	return;
      }
    }
    if(!strcmp(rptr->message, remarkStr) ||
       !strcmp(rptr->message, remarkStr2)){
      /* Don't add remark twice*/
      return;
    }
    rptr->next=malloc(sizeof(struct remark));
    rptr=rptr->next;
  }
  if(remarkType==0){
    strcpy(rptr->message, remarkStr);
  }
  else{
    strcpy(rptr->message, remarkStr2);
  }
  rptr->box=0;
  rptr->colour=0;
  rptr->next=NULL;
}
void
add_marker(char *marker, char *clone, int type){
  int cloneindex, markerindex;
  struct markerclone *mptr;
  struct markertop *clmptr;
  CLONE *clp;
  MARKER *mkp;

  if(!fppFind(acedata, clone, &cloneindex, cloneOrder)){
    printf("Clone %s not in FPC.  Marker %s not added\n", clone, marker);
    return;
  }

  /* Add clone to marker*/
  if(fppInsert(markerdata,marker,&markerindex,(void *) markerOrder)){
    /* New marker; set defaults and add first clone*/
    printf("Creating new marker %s...", marker);
    fflush(stdout);
    mkp=arrp(markerdata, markerindex, MARKER);
    mkp->cloneindex=cloneindex;
    mkp->count=1;
    mkp->colour=0;
    mkp->status=NEW;
    mkp->minicontigbox = mkp->textbox = mkp->textbox2 = 0;
    mkp->type=type;
    settime(&(mkp->creation_date));
    settime(&(mkp->modified_date));
    mkp->anchor = FALSE;
    mkp->pos = NULL;
    mkp->anchor_bin[0] = '\0';
    mkp->weak =  0;
    mkp->nextclone = NULL;
    mkp->remark = NULL;
    strcpy(mkp->marker,marker);
    sortoutmarkerindex();
    fppFind(markerdata,marker,&markerindex,(void *) markerOrder);
    update_contigs_list(markerindex);
    printf("done.\n");
  }
  else{
    /* Existing marker*/
    mkp=arrp(markerdata, markerindex, MARKER);
    if(mkp->cloneindex==cloneindex){
      /* Clone is already attached to marker*/
      if(mkp->type!=markeMRK && mkp->type!=markeBAC)
        add_e_add_remark(cloneindex, marker, 1);
      return;
    }
    if(mkp->nextclone==NULL){
      /* Second clone*/
      if(mkp->type!=markeMRK && mkp->type!=markeBAC)
        add_e_add_remark(cloneindex, marker, 0);
      mkp->nextclone=malloc(sizeof(struct markerclone));
      mkp->nextclone->cloneindex=cloneindex;
      mkp->nextclone->weak=0;
      mkp->nextclone->nextclone=NULL;
      mkp->count++;
      settime(&(mkp->modified_date));
      update_contigs_list(markerindex);
    }
    else{
      /* Third+ clone*/
      for(mptr=mkp->nextclone; mptr->nextclone!=NULL; mptr=mptr->nextclone){
        if(mptr->cloneindex==cloneindex){
          /* Clone is already attached to marker*/
          if(mkp->type!=markeMRK && mkp->type!=markeBAC)
            add_e_add_remark(cloneindex, marker, 1);
          return;
        }
      }
      if(mptr->cloneindex==cloneindex){
	/* Clone is already attached to marker*/
	if(mkp->type!=markeMRK && mkp->type!=markeBAC)
	  add_e_add_remark(cloneindex, marker, 1);
	return;
      }

      if(mkp->type!=markeMRK && mkp->type!=markeBAC)
        add_e_add_remark(cloneindex, marker, 0);
      mptr->nextclone=malloc(sizeof(struct markerclone));
      mptr->nextclone->cloneindex=cloneindex;
      mptr->nextclone->weak=0;
      mptr->nextclone->nextclone=NULL;
      mkp->count++;
      settime(&(mkp->modified_date));
      update_contigs_list(markerindex);
    }
  }

  /* Add marker to clone*/
  clp=arrp(acedata, cloneindex, CLONE);
  if(clp->marker==NULL){
    /*First marker for clone*/
    clp->marker=malloc(sizeof(struct markertop));
    clp->marker->markerindex=markerindex;
    clp->marker->weak=0;
    clp->marker->new=TRUE;
    strcpy(clp->marker->marker, marker);
    clp->marker->nextmarker=NULL;
    settime(&(clp->modified_date));
  }
  else{
    /* Second+ marker*/
    for(clmptr=clp->marker; clmptr->nextmarker!=NULL; clmptr=clmptr->nextmarker)
      ;
    clmptr->nextmarker=malloc(sizeof(struct markertop));
    clmptr->nextmarker->markerindex=markerindex;
    clmptr->nextmarker->weak=0;
    clmptr->nextmarker->new=TRUE;
    strcpy(clmptr->nextmarker->marker, marker);
    clmptr->nextmarker->nextmarker=NULL;
    settime(&(clp->modified_date));
  }
}
void add_as_markers()
{
    int mrktype;
    int i;
    BSS_HIT* hit;
    char mrkname[MARKER_SZ];
    gchar* prefix;
    int added = 0;

    mrktype = markeMRK;
    prefix = (gchar*)gtk_entry_get_text(GTK_ENTRY(prefix_entry_box));

    for (i = 0; i < arrayMax(g_bsi.bss_hits); i++)
    {
        hit = arrp(g_bsi.bss_hits,i,BSS_HIT);
        if (hit->filtered) continue;

        sprintf(mrkname,"%s%s",prefix,hit->data[BSS_MARKER].str);
        add_marker(mrkname,hit->data[BSS_CLONE].str,mrktype);
        added++;

    } 
    printf("added %d marker hits\n",added);
  
}
void add_as_remarks(int fp)
{
    int i;
    BSS_HIT* hit;
    gchar* prefix;
    char remark[50];
    int c;
    int added = 0;

    prefix = (gchar*)gtk_entry_get_text(GTK_ENTRY(prefix_entry_box));

    for (i = 0; i < arrayMax(g_bsi.bss_hits); i++)
    {
        hit = arrp(g_bsi.bss_hits,i,BSS_HIT);
        if (hit->filtered) continue;

        if(!fppFind(acedata, hit->data[BSS_CLONE].str, &c, cloneOrder))
        {
            printf("Clone %s not found in FPC.\n",hit->data[BSS_CLONE].str);
            return;
        }

        sprintf(remark,"%s%s",prefix,hit->data[BSS_MARKER].str);


        if (fp) addfpremark(remark,c);
        else addremark(remark,c);
        added++;
    }   
    printf("added %d %s\n",added,(fp ? "Fp_remarks" : "remarks"));
}
void do_add_hits(GtkWidget *widget, gpointer data)
{
    if (gtk_toggle_button_get_active((GtkToggleButton *)add_mrk_radio))
    {
        add_as_markers();
    }
    else if (gtk_toggle_button_get_active((GtkToggleButton *)add_rem_radio))
    {
        add_as_remarks(0);
    }
    else if (gtk_toggle_button_get_active((GtkToggleButton *)add_fprem_radio))
    {
        add_as_remarks(1);
    }    
}
void add_hits_to_fpc(GtkWidget* _w, gpointer _p)
{
    GtkWidget* vbox;
    GtkWidget* hbox1, *hbox2, *hbox3, *hbox4, *hbox5;
    GtkWidget* prefix_label;
    GtkWidget* add_mrk_label;
    GtkWidget* add_rem_label;
    GtkWidget* add_fprem_label;
    GtkWidget* apply_btn;


    add_hits_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_container_set_border_width (GTK_CONTAINER (add_hits_window), 5);
    gtk_window_set_title (GTK_WINDOW (add_hits_window), ("Add Hits to FPC"));
    gtk_window_set_policy (GTK_WINDOW (add_hits_window), FALSE, FALSE, TRUE);
    gtk_signal_connect (GTK_OBJECT (add_hits_window), "destroy",
		      GTK_SIGNAL_FUNC (close_add_hits_window), NULL);

    vbox = gtk_vbox_new (FALSE, 10);
    gtk_widget_show (vbox);
    gtk_container_add (GTK_CONTAINER (add_hits_window), vbox);

    hbox1 = gtk_hbox_new(FALSE,10);
    gtk_widget_show(hbox1);
    gtk_box_pack_start(GTK_BOX (vbox), hbox1,FALSE,FALSE,0);

    prefix_label = gtk_label_new ("Prefix:");
    gtk_widget_show (prefix_label);
    gtk_label_set_justify (GTK_LABEL (prefix_label), GTK_JUSTIFY_LEFT);
    gtk_box_pack_start(GTK_BOX (hbox1), prefix_label,FALSE,FALSE,0);

    prefix_entry_box = gtk_entry_new ();
    gtk_entry_set_text((GtkEntry*)prefix_entry_box,"BSS:");
    gtk_widget_set_usize(prefix_entry_box,120, 20 ); 
    gtk_widget_show (prefix_entry_box);
    gtk_box_pack_start(GTK_BOX (hbox1), prefix_entry_box,FALSE,FALSE,0);

    /**********************************************/

    hbox2 = gtk_hbox_new(FALSE,10);
    gtk_widget_show(hbox2);
    gtk_box_pack_start(GTK_BOX (vbox), hbox2,FALSE,FALSE,0);

    add_mrk_radio = gtk_radio_button_new(NULL);
    gtk_box_pack_start(GTK_BOX (hbox2), add_mrk_radio,FALSE,FALSE,0);
    gtk_widget_show(add_mrk_radio);

    add_mrk_label = gtk_label_new ("Add as Markers");
    gtk_widget_show (add_mrk_label);
    gtk_label_set_justify (GTK_LABEL (add_mrk_label), GTK_JUSTIFY_LEFT);
    gtk_box_pack_start(GTK_BOX (hbox2), add_mrk_label,FALSE,FALSE,0);

    /***************************************************/

    hbox3 = gtk_hbox_new(FALSE,10);
    gtk_widget_show(hbox3);
    gtk_box_pack_start(GTK_BOX (vbox), hbox3,FALSE,FALSE,0);

    add_rem_radio = gtk_radio_button_new_from_widget((GtkRadioButton*)add_mrk_radio);
    gtk_box_pack_start(GTK_BOX (hbox3), add_rem_radio,FALSE,FALSE,0);
    gtk_widget_show(add_rem_radio);

    add_rem_label = gtk_label_new ("Add as Remarks");
    gtk_widget_show (add_rem_label);
    gtk_label_set_justify (GTK_LABEL (add_rem_label), GTK_JUSTIFY_LEFT);
    gtk_box_pack_start(GTK_BOX (hbox3), add_rem_label,FALSE,FALSE,0);

    /**************************************************/

    hbox4 = gtk_hbox_new(FALSE,10);
    gtk_widget_show(hbox4);
    gtk_box_pack_start(GTK_BOX (vbox), hbox4,FALSE,FALSE,0);

    add_fprem_radio = gtk_radio_button_new_from_widget((GtkRadioButton*)add_mrk_radio);
    gtk_box_pack_start(GTK_BOX (hbox4), add_fprem_radio,FALSE,FALSE,0);
    gtk_widget_show(add_fprem_radio);

    add_fprem_label = gtk_label_new ("Add as FP remarks        ");
    gtk_widget_show (add_fprem_label);
    gtk_label_set_justify (GTK_LABEL (add_fprem_label), GTK_JUSTIFY_LEFT);
    gtk_box_pack_start(GTK_BOX (hbox4), add_fprem_label,FALSE,FALSE,0);

    /************************************************************/

    hbox5 = gtk_hbox_new(FALSE,10);
    gtk_widget_show(hbox5);
    gtk_box_pack_start(GTK_BOX (vbox), hbox5,FALSE,FALSE,0);

    apply_btn = gtk_button_new_with_label("Add");
    gtk_widget_set_usize(apply_btn,100 , 20); 
    gtk_widget_show(apply_btn);
    gtk_box_pack_start(GTK_BOX(hbox5), apply_btn, FALSE, FALSE, 0);
    gtk_signal_connect (GTK_OBJECT (apply_btn), "clicked",
		 GTK_SIGNAL_FUNC (do_add_hits), NULL);

     gtk_widget_show (add_hits_window);
}



static void r_show_help(){
  show_help("BSS Results Help", "bssResultsHelp");
}

void close_results(GtkWidget* _w,gpointer _p)
{
    if(results==0) return;

    gtk_widget_destroy(gexGraph2Widget(results));
    results=0;
    if (filter_window) 
    {
        gtk_widget_destroy((GtkWidget*) filter_window);
        filter_window = 0;
    }
}


static GtkItemFactoryEntry* menu_items;

static GtkItemFactoryEntry menu_items1[] = {
  { "/File", NULL, NULL, 0, "<Branch>" },
  { "/File/Save BSS", NULL, save_results, 0, NULL },
  { "/File/Save BSS as...", NULL, saveas_results, 0, NULL },
  { "/File/Save for spreadsheet", NULL, save_as_csv, 0, NULL },
  { "/File/Close", "", close_results, 0, NULL },

  { "/Analysis", NULL, NULL, 0, "<Branch>" },
  { "/Analysis/Filter hits", NULL, filter_hits, 0, NULL },
  { "/Analysis/View keyset of hit clones", NULL, view_hit_clone_keyset, 0, NULL },
  { "/Analysis/Add hits to FPC", NULL, add_hits_to_fpc, 0, NULL },

  { "/Columns", NULL, NULL, 0, "<Branch>" },
};

static GtkItemFactoryEntry menu_items2[] = {

  { "/Help", NULL, NULL, 0, "<LastBranch>" },
  { "/Help/Show help", NULL, r_show_help, 0, NULL },
};

static GtkItemFactory *item_factory;


void column_select_cb(gpointer callback_data,  guint i,  GtkWidget *widget)
{
    bss_columns_checked[i] = (bss_columns_checked[i] == 1 ? 0 : 1);
    gtk_clist_set_column_visibility((GtkCList*)r_clist3,i,bss_columns_checked[i]);

}
int right_bpress(GtkWidget *widget, GdkEvent *event){
   if(event->type==GDK_BUTTON_PRESS){
      GdkEventButton *bevent = (GdkEventButton *) event;
      if(bevent->button==3){
         gtk_menu_popup(GTK_MENU (widget),NULL,NULL,NULL, NULL,
                        bevent->button, bevent->time);
         return TRUE;/*the buck stops here.*/
      }
   }
   return FALSE;
}
/**********************************************************************************

        Display Result File

***********************************************************************************/

void display_result_file(int file_name_index)
{

   gchar* hitlist_titles[BSS_NUMTYPES];

   gchar *seqctg_titles2[]={"Sequence","Hits/#Ctgs","Best Ctg/#Hits"};


   //gchar *topten_titles[3]={"Contig","CST","CloneHits"};
   gchar *topten_titles[3]={"Contig","CloneHits"};

    GtkWidget * r_hbox;
    GtkWidget * r_wrapper_box;
    GtkWidget * r_scrolled_window;
    GtkWidget *r_event_box;
    GtkWidget *r_popup_menu;
    GtkWidget *r_save_item;
    GtkWidget *r_saveas_item;
    GtkWidget *r_quit_item;
    GtkWidget *r_print_item;
    GtkWidget *r_program_label;
    GtkWidget *r_inputDir_label;
    GtkWidget   *hboxtemp;

    GtkWidget *table;
    GtkWidget *vbox;
    GtkWidget *frame;
    GtkWidget *menubar;
    GtkWidget *colchoice;
  
   GtkAccelGroup *accel_group;

    char aString[1024];
    char* path = NULL;
    int k=0;
    int i;
    int nm1, nm2, nm;
    GtkItemFactoryEntry gife;
    BSS_TYPE bsscol;

    if (filter_window)
    {
        gtk_widget_destroy((GtkWidget*)filter_window);
        filter_window = 0;
    }
    if((file_name_index<0)||(file_name_index>num_displayed_files)) return;

    r_selected_row1=-1;
    r_selected_row2=-1;
    r_selected_row3=-1;
    r_max_contig=max_contig;

    destroy_bss_instance(&g_bsi);

    if(graphActivate(results))
    {
        graphPop();
        gtk_widget_destroy(r_table);
        graphRetitle(displayed_files[file_name_index].name);
    }
    else if (NoPop)
    {
        return;
    }
    else
    {
                                                     /* pos x, y; width, leng */
        results = graphCreate (TEXT_FIT,displayed_files[file_name_index].name,0,0,0,0);
        gtk_window_set_policy(GTK_WINDOW(gexGraph2Widget(results)), FALSE, FALSE, TRUE);
    }
    //gtk_window_set_resizable (GTK_WINDOW(gexGraph2Widget(results)),TRUE);

    //reset_prevsortlist();
    

    path = g_strjoin(G_DIR_SEPARATOR_S, GETCURDIR, "BSS_results",
                    displayed_files[file_name_index].name, NULL);


    if (!read_bss_file(path,&g_bsi))
    {
        fprintf(stderr," BSS file %s\n",path);
        return;
    }
    
   gtk_signal_connect(GTK_OBJECT(gexGraph2Widget(results)),"delete_event",
                      GTK_SIGNAL_FUNC(close_results),(gpointer)0);
   r_wrapper_box=gexGraphHbox(results);
   r_table=gtk_table_new(4,2,FALSE);

   nm1 = sizeof(menu_items1)/sizeof(GtkItemFactoryEntry);
   nm2 = sizeof(menu_items2)/sizeof(GtkItemFactoryEntry);
   nm = nm1 + nm2 + bss_ncols*(sizeof(GtkItemFactoryEntry));
   menu_items = (GtkItemFactoryEntry*)malloc(nm*sizeof(GtkItemFactoryEntry));
   memset(menu_items,0,nm*sizeof(GtkItemFactoryEntry));
   memcpy(menu_items,menu_items1,nm1*sizeof(GtkItemFactoryEntry));
   nm = nm1;
   for (i = 0; i < bss_ncols - BSS_HIDDEN_FIELDS; i++)
   {
        bsscol = bss_column_order[i];
        gife.path = g_strdup_printf("/Columns/%s",bss_headers[bsscol]);
        gife.accelerator = NULL;
        gife.callback = column_select_cb;
        gife.callback_action = i;
        gife.item_type = strdup("<ToggleItem>");
        memcpy(&menu_items[nm],&gife,sizeof(GtkItemFactoryEntry));
        nm++;	          
   }
   memcpy(&menu_items[nm],menu_items2,nm2*sizeof(GtkItemFactoryEntry));

   accel_group = gtk_accel_group_new (); 
   item_factory = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "<main>",
                                       accel_group);
   gtk_item_factory_create_items (item_factory, nm, menu_items, NULL);


   gtk_window_add_accel_group (GTK_WINDOW(gexGraph2Widget(results)), accel_group);
   menubar = gtk_item_factory_get_widget (item_factory, "<main>");


   r_hbox=gtk_hbox_new(FALSE,1);
   gtk_box_pack_start (GTK_BOX (r_hbox), menubar, TRUE, TRUE, 0);
   gtk_widget_show (menubar);
   gtk_table_attach_defaults(GTK_TABLE(r_table),r_hbox,0,2,k,k+1); k++;

   /*Print headings*/
   table=gtk_table_new(1,2,FALSE);
   frame=gtk_frame_new("Result stats");
   gtk_container_set_border_width (GTK_CONTAINER (frame), 10);
   vbox=gtk_vbox_new(FALSE, 3);
   gtk_container_add(GTK_CONTAINER(frame), vbox);
   gtk_table_attach_defaults(GTK_TABLE(table),frame,0,1,0,1);


   r_hbox=gtk_hbox_new(FALSE,0);
   sprintf(aString,"Query file: ");
   gtk_box_pack_start(GTK_BOX(r_hbox),gtk_label_new(aString),FALSE,FALSE,0);
   r_inputFile_label=gtk_label_new(g_bsi.query_file);
   gtk_box_pack_start(GTK_BOX(r_hbox),r_inputFile_label,FALSE,FALSE,0);
   gtk_box_pack_start(GTK_BOX(vbox), r_hbox, FALSE, FALSE, 0);

   r_hbox=gtk_hbox_new(FALSE,0);
   sprintf(aString,"Query directory: ");
   gtk_box_pack_start(GTK_BOX(r_hbox),gtk_label_new(aString),FALSE,FALSE,0);
   r_inputDir_label=gtk_label_new(g_bsi.query_dir);
   gtk_box_pack_start(GTK_BOX(r_hbox),r_inputDir_label,FALSE,FALSE,0);
   gtk_box_pack_start(GTK_BOX(vbox), r_hbox, FALSE, FALSE, 0);


   r_hbox=gtk_hbox_new(FALSE,0);
   sprintf(aString,"Database: %d files from: ", g_bsi.dbs->len);
   gtk_box_pack_start(GTK_BOX(r_hbox),gtk_label_new(aString),FALSE,FALSE,0);
   r_dbDir_label=gtk_label_new(g_bsi.db_dir);
   gtk_box_pack_start(GTK_BOX(r_hbox),r_dbDir_label,FALSE,FALSE,0);
   gtk_box_pack_start(GTK_BOX(vbox), r_hbox, FALSE, FALSE, 0);

   r_hbox=gtk_hbox_new(FALSE,0);
   sprintf(aString,"Program: ");
   gtk_box_pack_start(GTK_BOX(r_hbox),gtk_label_new(aString),FALSE,FALSE,0);
   r_program_label=gtk_label_new(g_bsi.program == BLAT ? "BLAT"
                                       : (g_bsi.program == MEGABLAST ? "MegaBLAST"
                                       : "BLAST"));
   gtk_box_pack_start(GTK_BOX(r_hbox),r_program_label,FALSE,FALSE,0);
   gtk_box_pack_start(GTK_BOX(vbox), r_hbox, FALSE, FALSE, 0);


   if (g_bsi.program != BLAT) {
      r_hbox=gtk_hbox_new(FALSE,0);
      sprintf(aString,"Expectation Value: ");
      gtk_box_pack_start(GTK_BOX(r_hbox),gtk_label_new(aString),FALSE,FALSE,0);
      r_eval_label=gtk_label_new(g_bsi.cutoff_str);
      gtk_box_pack_start(GTK_BOX(r_hbox),r_eval_label,FALSE,FALSE,0);
      gtk_box_pack_start(GTK_BOX(vbox), r_hbox, FALSE, FALSE, 0);

      r_hbox=gtk_hbox_new(FALSE,0);
      sprintf(aString,"Additional Parameters: ");
      gtk_box_pack_start(GTK_BOX(r_hbox),gtk_label_new(aString),FALSE,FALSE,0);
      r_xtraParm_label=gtk_label_new(g_bsi.xtraParm);
      gtk_box_pack_start(GTK_BOX(r_hbox),r_xtraParm_label,FALSE,FALSE,0);
      gtk_box_pack_start(GTK_BOX(vbox), r_hbox, FALSE, FALSE, 0);
   }
   else {
      r_hbox=gtk_hbox_new(FALSE,0);
      sprintf(aString,"Score: ");
      gtk_box_pack_start(GTK_BOX(r_hbox),gtk_label_new(aString),FALSE,FALSE,0);
      sprintf(aString,"%d",g_bsi.blat_score);
      r_eval_label=gtk_label_new(aString);
      gtk_box_pack_start(GTK_BOX(r_hbox),r_eval_label,FALSE,FALSE,0);
      gtk_box_pack_start(GTK_BOX(vbox), r_hbox, FALSE, FALSE, 0);

      r_hbox=gtk_hbox_new(FALSE,0);
      sprintf(aString,"Additional BLAT Parameters: ");
      gtk_box_pack_start(GTK_BOX(r_hbox),gtk_label_new(aString),FALSE,FALSE,0);
      r_xtraParm_label=gtk_label_new(g_bsi.xtraParm);
      gtk_box_pack_start(GTK_BOX(r_hbox),r_xtraParm_label,FALSE,FALSE,0);
      gtk_box_pack_start(GTK_BOX(vbox), r_hbox, FALSE, FALSE, 0);
   }

   r_hbox=gtk_hbox_new(FALSE,0);
   sprintf(aString,"Number of Hits: ");
   gtk_box_pack_start(GTK_BOX(r_hbox),gtk_label_new(aString),FALSE,FALSE,0);
   sprintf(aString,"%d",arrayMax(g_bsi.bss_hits));
   r_numOfHits_label=gtk_label_new(aString);
   gtk_box_pack_start(GTK_BOX(r_hbox),r_numOfHits_label,FALSE,FALSE,0);
   sprintf(aString," from ");
   gtk_box_pack_start(GTK_BOX(r_hbox),gtk_label_new(aString),FALSE,FALSE,0);
   sprintf(aString,"%d",num_queries(&g_bsi));
   r_queryFilSecWithHits_label=gtk_label_new(aString);
   gtk_box_pack_start(GTK_BOX(r_hbox),r_queryFilSecWithHits_label,FALSE,FALSE,0);
   sprintf(aString," sequences.  ");

   gtk_box_pack_start(GTK_BOX(r_hbox),gtk_label_new(aString),FALSE,FALSE,0);
   sprintf(aString,"%d",g_bsi.num_initial_queries);
   r_queryFilSec_label=gtk_label_new(aString);
   gtk_box_pack_start(GTK_BOX(r_hbox),r_queryFilSec_label,FALSE,FALSE,0);

   sprintf(aString," sequences in query file.");

   gtk_box_pack_start(GTK_BOX(r_hbox),gtk_label_new(aString),FALSE,FALSE,0);
   gtk_box_pack_start(GTK_BOX(vbox), r_hbox, FALSE, FALSE, 0);

    for (i = 0; i < g_bsi.old_filter_history->len; i++)
    {
        sprintf(aString,"Filter:%s",g_array_index(g_bsi.old_filter_history,char*,i));
        hboxtemp = gtk_hbox_new(FALSE,0);
        gtk_box_pack_start(GTK_BOX(hboxtemp),gtk_label_new(aString),FALSE,FALSE,0);
        gtk_box_pack_start(GTK_BOX(vbox), hboxtemp, FALSE, FALSE, 0);
    }

   vbox=gtk_vbox_new(FALSE,1);


   /******************   Query file section table  *****************************/

   r_scrolled_window=gtk_scrolled_window_new(NULL,NULL);
   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (r_scrolled_window),
                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
   gtk_widget_set_usize(r_scrolled_window,300,100);

   r_clist1=NULL;

   r_clist1=gtk_clist_new_with_titles(G_N_ELEMENTS(seqctg_titles2),
                                         seqctg_titles2);
   gtk_clist_set_column_auto_resize(GTK_CLIST(r_clist1), 4, TRUE);
   gtk_clist_column_titles_active(GTK_CLIST(r_clist1));

   gtk_signal_connect(GTK_OBJECT(GTK_CLIST(r_clist1)->column[0].button),
                         "button_press_event",GTK_SIGNAL_FUNC(querylist_column_click),
                         (gpointer)1);
   gtk_signal_connect(GTK_OBJECT(GTK_CLIST(r_clist1)->column[1].button),
                      "button_press_event",GTK_SIGNAL_FUNC(querylist_column_click),(gpointer)2);
   gtk_signal_connect(GTK_OBJECT(GTK_CLIST(r_clist1)->column[2].button),
                      "button_press_event",GTK_SIGNAL_FUNC(querylist_column_click),(gpointer)3);


   gtk_clist_set_column_width (GTK_CLIST(r_clist1), 0, 100);
   gtk_clist_set_column_width (GTK_CLIST(r_clist1), 1, 100);
   gtk_clist_set_column_width (GTK_CLIST(r_clist1), 2, 100);

   refresh_query_table();

   gtk_container_add(GTK_CONTAINER(r_scrolled_window),r_clist1);
   gtk_box_pack_start(GTK_BOX(vbox), r_scrolled_window, FALSE, FALSE, 0);

   /********************* Contig table ************************/

   r_scrolled_window=gtk_scrolled_window_new(NULL,NULL);
   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (r_scrolled_window),
                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
   gtk_widget_set_usize(r_scrolled_window,300,100);

   r_clist2=NULL;
   r_clist2=gtk_clist_new_with_titles(2,topten_titles);
   gtk_clist_column_titles_active(GTK_CLIST(r_clist2));
   gtk_signal_connect(GTK_OBJECT(r_clist2), "select_row",
                      GTK_SIGNAL_FUNC(r_display_contig),
                      0);
   gtk_signal_connect(GTK_OBJECT(GTK_CLIST(r_clist2)->column[0].button),
                      "button_press_event",GTK_SIGNAL_FUNC(ctglist_column_click),(gpointer)1);
   gtk_signal_connect(GTK_OBJECT(GTK_CLIST(r_clist2)->column[1].button),
                      "button_press_event",GTK_SIGNAL_FUNC(ctglist_column_click),(gpointer)2);
  // gtk_signal_connect(GTK_OBJECT(GTK_CLIST(r_clist2)->column[2].button),
   //                   "button_press_event",GTK_SIGNAL_FUNC(ctglist_column_click),(gpointer)3);
   gtk_clist_set_column_width (GTK_CLIST(r_clist2), 0, 100);
   gtk_clist_set_column_width (GTK_CLIST(r_clist2), 1, 65);

   refresh_contig_table();

   gtk_container_add(GTK_CONTAINER(r_scrolled_window),r_clist2);
   gtk_box_pack_start(GTK_BOX(vbox), r_scrolled_window, FALSE, FALSE, 0);

   gtk_table_attach_defaults(GTK_TABLE(table),vbox,1,2,0,1);
   gtk_table_attach_defaults(GTK_TABLE(r_table),table,0,2,k,k+1); k++;

   /******************************* Hit table ******************************/

   r_hbox = gtk_hbox_new(FALSE,0);
   r_scrolled_window=gtk_scrolled_window_new(NULL,NULL);
   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (r_scrolled_window),
                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
   gtk_widget_set_usize(r_scrolled_window,870,400);
   gtk_box_pack_start(GTK_BOX(r_hbox),r_scrolled_window,TRUE,TRUE,0);

   for (i = 0; i < bss_ncols - BSS_HIDDEN_FIELDS; i++)
   {
       hitlist_titles[i] = g_strdup(bss_headers[bss_column_order[i]]);  
   }

   r_clist3=gtk_clist_new_with_titles(bss_ncols - BSS_HIDDEN_FIELDS,hitlist_titles);

   gtk_signal_connect(GTK_OBJECT(r_clist3), "select_row",
                            GTK_SIGNAL_FUNC(r_selection_made),
                            0);

   for (i = 0; i < bss_ncols - BSS_HIDDEN_FIELDS; i++)
   {
        gtk_signal_connect(GTK_OBJECT(GTK_CLIST(r_clist3)->column[i].button),
                            "button_press_event",GTK_SIGNAL_FUNC(hitlist_column_click),(gpointer)&bss_column_order[i]);        
        gtk_clist_set_column_width (GTK_CLIST(r_clist3), i, bss_col_widths_px[bss_column_order[i]]);
   }

   refresh_hitlist();

   gtk_container_add(GTK_CONTAINER(r_scrolled_window),r_clist3);
   gtk_table_attach_defaults(GTK_TABLE(r_table),r_hbox,0,2,k,k+1); k++;

   r_event_box=gtk_event_box_new();
   gtk_container_add(GTK_CONTAINER(r_wrapper_box),r_event_box);
   gtk_container_add(GTK_CONTAINER(r_event_box),r_table);

   gtk_table_set_row_spacings(GTK_TABLE(r_table),6);
   gtk_table_set_col_spacings(GTK_TABLE(r_table),5);

   /*Add popup menu.*/
   r_popup_menu=gtk_menu_new();
   r_quit_item=gtk_menu_item_new_with_label("Close");
   r_print_item=gtk_menu_item_new_with_label("Save hits as...");
   r_save_item=gtk_menu_item_new_with_label("Save");
   r_saveas_item=gtk_menu_item_new_with_label("Save as...");

   gtk_menu_append(GTK_MENU(r_popup_menu),r_quit_item);
   gtk_menu_append(GTK_MENU(r_popup_menu),r_print_item);
   gtk_menu_append(GTK_MENU(r_popup_menu),r_save_item);
   gtk_menu_append(GTK_MENU(r_popup_menu),r_saveas_item);

   gtk_signal_connect_object(GTK_OBJECT(r_quit_item),"activate",
                             GTK_SIGNAL_FUNC(close_results),(gpointer)0);
   gtk_signal_connect_object(GTK_OBJECT(r_print_item),"activate",
                             GTK_SIGNAL_FUNC(save_as_csv),NULL);
   gtk_signal_connect_object(GTK_OBJECT(r_save_item),"activate",
                             GTK_SIGNAL_FUNC(save_results),(gpointer)0);
   gtk_signal_connect_object(GTK_OBJECT(r_saveas_item),"activate",
                             GTK_SIGNAL_FUNC(saveas_results),(gpointer)0);

   gtk_signal_connect_object(GTK_OBJECT(r_event_box),"button_press_event",
                             GTK_SIGNAL_FUNC(right_bpress),GTK_OBJECT(r_popup_menu));
   gtk_widget_show(r_quit_item);
   gtk_widget_show(r_print_item);
   gtk_widget_show(r_save_item);
   gtk_widget_show(r_saveas_item);

   for (i = 0; i < bss_ncols - BSS_HIDDEN_FIELDS ; i++)
   {
       bss_columns_checked[i] = 0;  // because the first set_active will toggle this
       colchoice = gtk_item_factory_get_widget_by_action(item_factory,i); 
       gtk_check_menu_item_set_active((GtkCheckMenuItem*)colchoice,1);
       if (BSS_SEQCTG == bss_column_order[i]) 
       {
          // we want this one off by default
          gtk_check_menu_item_set_active((GtkCheckMenuItem*)colchoice,0);
       }
   }
    

   gtk_widget_show_all(gexGraph2Widget(results));

   if (path != NULL)
       g_free(path);


   return;
}

int num_queries(BSS_INST* pbsi)
{
	int count = 0;
    QUERY_HIT_DATA* pqd;
	int i;

    for (i = 0; i < arrayMax(pbsi->bss_queryhits); ++i) 
    {
        pqd = arrayp(pbsi->bss_queryhits,i,QUERY_HIT_DATA);
        if (pqd->hits == 0) continue;
		count++;
	}
	return count;
}

void close_filter_window(GtkWidget *widget, gpointer data)
{
    if (filter_window)
    {
        gtk_widget_destroy((GtkWidget*)filter_window);
        filter_window = 0;
    }
}


void filter_history_pop()
{
    int len;
    len = fpc_history_list->rows;
    gtk_clist_remove((GtkCList*)fpc_history_list,len-1);
    len = g_bsi.filter_history->len;
    g_array_remove_index(g_bsi.filter_history,len-1);
}
void init_fpc_history_list()
{
    int i;
    BSS_FILTER bf;
    gchar* history[3];
   
    
    for (i = 0; i < g_bsi.filter_history->len; i++)
    {
        bf = g_array_index(g_bsi.filter_history,BSS_FILTER,i);
        history[0] = strdup(bf.type);
        history[1] = strdup(bf.search);
        history[2] = strdup(bf.results);
        gtk_clist_append((GtkCList*)fpc_history_list,history);
    }
}
void filter_history_add(gchar** record)
{
    BSS_FILTER filter;
    gtk_clist_append((GtkCList*)fpc_history_list,record);

    filter.type = strdup(record[0]);
    filter.search = strdup(record[1]);
    filter.results = strdup(record[2]);

    g_array_append_val(g_bsi.filter_history,filter);
}
char cmpchar(int cmp)
{
    switch (cmp)
    {
        case 1:
            return '>';
        case -1:
            return '<';
        case 0:
            return '=';
        default:
            assert(0);
    }
    assert(0);
    return 0;
}

void floating_point_filter(BSS_TYPE type, gchar* filter, int cmp)
{
    double thresh = strtod(filter,0);
    int i;
    double val;
    BSS_HIT* hit;
    int filtered;
    
    for (i = 0; i < arrayMax(g_bsi.bss_hits); i++)
    {
        hit = arrp(g_bsi.bss_hits,i,BSS_HIT);
        if (hit->filtered) continue;
        val = strtod(hit->data[type].str,0);
        filtered = 0;
        switch(cmp)
        {
            case 0:
                if (val != thresh)
                {
                    filtered = 1;
                }    
                break;
            case -1:
                if (val > thresh)
                {
                    filtered = 1;
                }    
                break;
            case 1:
                if (val < thresh)
                {
                    filtered = 1;
                }    
                break;
            default:
                assert(0);
        }  
        if (filtered)
        {
            hit->filtered = g_bsi.filter_history->len + 1;
        }
    }    
}
void integer_filter(BSS_TYPE type, gchar* filter, int cmp)
{
    int thresh = strtol(filter,0,10);
    int i;
    int val;
    BSS_HIT* hit;
    int filtered;  

    for (i = 0; i < arrayMax(g_bsi.bss_hits); i++)
    {
        hit = arrp(g_bsi.bss_hits,i,BSS_HIT);
        if (hit->filtered) continue;

        val = hit->data[type].i;
        filtered = 0;
        switch(cmp)
        {
            case 0:
                if (val != thresh)
                {
                    filtered = 1;
                }    
                break;
            case -1:
                if (val >= thresh)
                {
                    filtered = 1;
                }    
                break;
            case 1:
                if (val <= thresh)
                {
                    filtered = 1;
                }    
                break;
            default:
                assert(0);
        }  
        if (filtered)
        {
            hit->filtered = g_bsi.filter_history->len + 1; 
        }
    }    
}
int bss_pattern_match(char* str, char* pat)
{
    char pat1[100];
    char* ptr = pat1;
    char* ptr2;
    int ret = 0;


    if (!strstr(pat,"*"))
    {
        if (!strcmp(str,pat)) ret = 1;
    }
    else
    { 
        strcpy(pat1,pat);
        if (pat1[0] == '*')
        {
            if (strlen(pat1) > 2 && pat1[strlen(pat1) - 1] == '*')
            {
                // *xyz*
                ptr++;
                ptr[strlen(ptr)-1] = 0;
                if (strstr(str,ptr)) ret = 1;
            }
            else
            {
                // *xyz
                ptr++;
                ptr2 = str + strlen(str) - strlen(ptr);
                if (ptr2 > str && !strcmp(ptr2,ptr)) ret = 1;
            }
        }
        else if ( pat1[strlen(pat1) - 1] == '*')
        {
            // xyz*
            pat1[strlen(pat1) - 1] = 0;
            if (!strncmp(str,pat1,strlen(pat1))) ret = 1;
        }
    }
    return ret;
}
void do_string_filter(BSS_TYPE type, gchar* filter)
{
    int i;
    BSS_HIT* hit;
    
    for (i = 0; i < arrayMax(g_bsi.bss_hits); i++)
    {
        hit = arrp(g_bsi.bss_hits,i,BSS_HIT);
        if (hit->filtered) continue;
        
        if (!bss_pattern_match(hit->data[type].str,filter))
        {        
            hit->filtered = g_bsi.filter_history->len + 1;
        }
    }    
}
void string_filter()
{
    gchar* filter;
    BSS_TYPE type;
    gchar* history[3];

    type = bss_column_order[g_filter_field];
    filter = (gchar*)gtk_entry_get_text(GTK_ENTRY(g_string_filter));    

    do_string_filter(type,filter);
    
    history[0] = g_strdup(bss_headers[type]); 
    history[1] = g_strdup_printf("%s",filter);
    history[2] = g_strdup_printf("%d",count_unfiltered());

    filter_history_add(history);
}

void numeric_filter()
{
    gchar* filter;
    BSS_TYPE type;
    gchar* history[3];

    filter = (gchar*)gtk_entry_get_text(GTK_ENTRY(g_numeric_filter));    

    type = bss_column_order[g_filter_field];
    if (type == BSS_EVALUE)
    {
        floating_point_filter(type,filter,g_cmp);
    }
    else if (type == BSS_CTGLR)
    {
        integer_filter(BSS_CTG,filter,g_cmp);
    }
    else
    {
        integer_filter(type,filter,g_cmp);
    }
    
    history[0] = g_strdup(bss_headers[type]); 
    history[1] = g_strdup_printf("%c %s",cmpchar(g_cmp),filter);
    history[2] = g_strdup_printf("%d",count_unfiltered());

    filter_history_add(history);
}   
void minctghits_filter()
{
    int i, ctg, minhits;
    BSS_HIT* hit;
    struct ctgsHitlistType* pCtgHit;
    FPC_TREE qtree;
    QUERY_HIT_DATA *pqd;
    gchar* history[3];

    minhits = strtol(gtk_entry_get_text(GTK_ENTRY(g_minctghits_filter)),0,10); 

    /* put the query hit data into a tree for faster access */

    init_fpc_tree(&qtree);   
    for (i = 0; i < arrayMax(g_bsi.bss_queryhits); i++)
    {
        pqd = arrp(g_bsi.bss_queryhits,i,QUERY_HIT_DATA);
        fpc_tree_insert_node(&qtree, (void*)pqd, pqd->query);
    }

    for (i = 0; i < arrayMax(g_bsi.bss_hits); i++)
    {
        hit = arrp(g_bsi.bss_hits,i,BSS_HIT);
        if (hit->filtered) continue;

        pqd = fpc_tree_find_data(&qtree,hit->data[BSS_MARKER].str);

        if (!pqd) assert(0);

        ctg = hit->data[BSS_CTG].i;
        pCtgHit = find_ctg_hit(ctg, pqd->ctgsHitlist);
        if (!pCtgHit) assert(0);

        if (pCtgHit->cloneHits < minhits)
        {        
            hit->filtered = g_bsi.filter_history->len + 1;   
        }
    }  

    clear_fpc_tree(&qtree,0);  

    history[0] = g_strdup("Hits per ctg"); 
    history[1] = g_strdup_printf(">= %d",minhits);
    history[2] = g_strdup_printf("%d",count_unfiltered());

    filter_history_add(history);
}
void maxctgshit_filter()
{
    int i, maxhits;
    BSS_HIT* hit;
    FPC_TREE qtree;
    QUERY_HIT_DATA *pqd;
    gchar* history[3];

    maxhits = strtol(gtk_entry_get_text(GTK_ENTRY(g_maxctgshit_filter)),0,10); 

   /* put the query hit data into a tree for faster access */

    init_fpc_tree(&qtree);   
    for (i = 0; i < arrayMax(g_bsi.bss_queryhits); i++)
    {
        pqd = arrp(g_bsi.bss_queryhits,i,QUERY_HIT_DATA);
        fpc_tree_insert_node(&qtree, (void*)pqd, pqd->query);
    }

    for (i = 0; i < arrayMax(g_bsi.bss_hits); i++)
    {
        hit = arrp(g_bsi.bss_hits,i,BSS_HIT);
        if (hit->filtered) continue;

        pqd = fpc_tree_find_data(&qtree,hit->data[BSS_MARKER].str);

        if (!pqd) assert(0);

        if (pqd->numCtgsHit > maxhits)
        {        
            hit->filtered = g_bsi.filter_history->len + 1;
        }
    }  

    clear_fpc_tree(&qtree,0);  

    history[0] = g_strdup("Contigs hit"); 
    history[1] = g_strdup_printf("<= %d",maxhits);
    history[2] = g_strdup_printf("%d",count_unfiltered());

    filter_history_add(history);
}
void maxhits_filter()
{
    int i, maxhits;
    BSS_HIT* hit;
    FPC_TREE qtree;
    QUERY_HIT_DATA *pqd;
    gchar* history[3];

    maxhits = strtol(gtk_entry_get_text(GTK_ENTRY(g_maxhits_filter)),0,10); 

   /* must put the query hit data into a tree for faster access */

    init_fpc_tree(&qtree);   
    for (i = 0; i < arrayMax(g_bsi.bss_queryhits); i++)
    {
        pqd = arrp(g_bsi.bss_queryhits,i,QUERY_HIT_DATA);
        fpc_tree_insert_node(&qtree, (void*)pqd, pqd->query);
    }

    for (i = 0; i < arrayMax(g_bsi.bss_hits); i++)
    {
        hit = arrp(g_bsi.bss_hits,i,BSS_HIT);
        if (hit->filtered) continue;
        pqd = fpc_tree_find_data(&qtree,hit->data[BSS_MARKER].str);
        if (!pqd) assert(0);

        if (pqd->hits > maxhits)
        {        
            hit->filtered = g_bsi.filter_history->len + 1;
        }
    }  

    clear_fpc_tree(&qtree,0);  

    history[0] = g_strdup("Total hits"); 
    history[1] = g_strdup_printf("<= %d",maxhits);
    history[2] = g_strdup_printf("%d",count_unfiltered());

    filter_history_add(history);
}
void ctgends_filter()
{
    int i;
    BSS_HIT* hit;
    gchar* history[3];

    // get the contig LR adjusted for the fromendInt setting
    bss_check_contigs(&g_bsi);

    for (i = 0; i < arrayMax(g_bsi.bss_hits); i++)
    {
        hit = arrp(g_bsi.bss_hits,i,BSS_HIT);
        if (hit->filtered) continue;

        if (0 == strcmp(hit->data[BSS_LR].str,"-"))
        {        
            hit->filtered = g_bsi.filter_history->len + 1;
        }
    }  

    history[0] = g_strdup("CtgEnds"); 
    history[1] = strdup("");
    history[2] = g_strdup_printf("%d",count_unfiltered());

    filter_history_add(history);
}
void apply_btn_callback(GtkWidget *widget, gpointer data)
{
    if (gtk_toggle_button_get_active((GtkToggleButton*)g_string_radio))
    {
        gtk_menu_item_activate((GtkMenuItem*)gtk_menu_get_active((GtkMenu*)g_string_options));
        string_filter();
    }
    else if (gtk_toggle_button_get_active((GtkToggleButton*)g_numeric_radio))
    {
        gtk_menu_item_activate((GtkMenuItem*)gtk_menu_get_active((GtkMenu*)g_numeric_options));
        numeric_filter();
    }
    else if (gtk_toggle_button_get_active((GtkToggleButton*)g_minctghits_radio))
    {
        minctghits_filter();
    }
    else if (gtk_toggle_button_get_active((GtkToggleButton*)g_maxctgshit_radio))
    {
        maxctgshit_filter();
    }
    else if (gtk_toggle_button_get_active((GtkToggleButton*)g_maxhits_radio))
    {
        maxhits_filter();
    }
    else if (gtk_toggle_button_get_active((GtkToggleButton*)g_ctgends_radio))
    {
        Pz.fromendInt = strtol(gtk_entry_get_text(GTK_ENTRY(g_fromend_filter)),0,10);
        ctgends_filter();
    }
    else
    {
        assert(0);
    }


    compute_ctg_query_tables(&g_bsi,0, arrayMax(g_bsi.bss_hits) - 1);
    refresh_hitlist();
    refresh_query_table();
    refresh_contig_table();
}
void cmp_set(GtkWidget *widget, gpointer data)
{
    if ((int)data == 0) g_cmp = -1;
    else if ((int)data == 1) g_cmp = 0;
    else if ((int)data == 2) g_cmp = 1;
    else assert(0);
}
void undo_btn_callback(GtkWidget *widget, gpointer data)
{
    int i;
    BSS_HIT* pbh;
    int fnum = g_bsi.filter_history->len;

    for (i = 0; i < arrayMax(g_bsi.bss_hits); i++)
    {
        pbh = arrp(g_bsi.bss_hits,i,BSS_HIT);
        if (pbh->filtered == fnum) pbh->filtered = 0;
    }
    filter_history_pop();

    compute_ctg_query_tables(&g_bsi,0, arrayMax(g_bsi.bss_hits) - 1); 
    refresh_hitlist();
    refresh_query_table();
    refresh_contig_table();
}
void filter_field_select(GtkWidget *widget, gpointer data)
{
    g_filter_field = (int)data;
}
int bss_is_string_field(int i)
{
    BSS_TYPE type = bss_column_order[i];

        if (type ==  BSS_QSTART ||
            type ==  BSS_QEND ||
            type ==  BSS_QLEN ||
            type ==  BSS_TSTART ||
            type == BSS_TEND ||
            type == BSS_TLEN ||
            type == BSS_SEQCTG ||
            type == BSS_HITLEN ||
            type == BSS_SCORE ||
            type == BSS_CTG ||
            type == BSS_CTGLR ||
            type == BSS_EVALUE ||
            type == BSS_IDENTITY ||
            type == BSS_MATCH  ||
            type == BSS_INTRON 
            )
        {
            return 0;            
        }
    return 1;
}
void filter_hits(GtkWidget* _w, gpointer _p)
{
    GtkWidget* vbox;
    GtkWidget* hbox1, *hbox2, *hbox3, *hbox5, *hbox6, *hbox7, *hbox8;
    GtkWidget* string_filter_label,*numeric_filter_label, *fromend_filter_label;
    GtkWidget* minctghits_filter_label,*maxctgshit_filter_label, *maxhits_filter_label, *ctgends_filter_label;
    GtkWidget* menu_items[BSS_NUMTYPES];
    GtkWidget* apply_btn, *undo_btn;
    GtkWidget* cmp1, *cmp2, *cmp3, *cmp_options;
    int i;
    gchar* history_titles[3] = {"field","search","#results"};
    
    if (filter_window) 
    {
        return;
    }


    g_cmp_menu = gtk_option_menu_new ();
    gtk_widget_show (g_cmp_menu);
    cmp_options = gtk_menu_new ();
    cmp1 = gtk_menu_item_new_with_label("<");
    cmp2 = gtk_menu_item_new_with_label("=");
    cmp3 = gtk_menu_item_new_with_label(">");
    gtk_widget_show (cmp1);
    gtk_widget_show (cmp2);
    gtk_widget_show (cmp3);
    gtk_container_add (GTK_CONTAINER (cmp_options),cmp1);
    gtk_container_add (GTK_CONTAINER (cmp_options),cmp2);
    gtk_container_add (GTK_CONTAINER (cmp_options),cmp3);
    gtk_signal_connect (GTK_OBJECT (cmp1), "activate",GTK_SIGNAL_FUNC (cmp_set),(gpointer)0);
    gtk_signal_connect (GTK_OBJECT (cmp2), "activate",GTK_SIGNAL_FUNC (cmp_set),(gpointer)1);
    gtk_signal_connect (GTK_OBJECT (cmp3), "activate",GTK_SIGNAL_FUNC (cmp_set),(gpointer)2);
    g_cmp = -1;
    gtk_option_menu_set_menu (GTK_OPTION_MENU (g_cmp_menu), cmp_options);


    g_numeric_menu = gtk_option_menu_new ();
    gtk_widget_show (g_numeric_menu);

    g_string_menu = gtk_option_menu_new ();
    gtk_widget_show (g_string_menu);

    g_string_options = gtk_menu_new ();
    g_numeric_options = gtk_menu_new ();

    for (i = 0; i < bss_ncols - BSS_HIDDEN_FIELDS; i++)
    {
        menu_items[i] = gtk_menu_item_new_with_label(bss_headers[bss_column_order[i]]);
        gtk_widget_show (menu_items[i]);

        if (bss_is_string_field(i))
        {
            gtk_container_add (GTK_CONTAINER (g_string_options), menu_items[i]);
        }
        else
        {
            gtk_container_add (GTK_CONTAINER (g_numeric_options), menu_items[i]);
        }
        gtk_signal_connect (GTK_OBJECT (menu_items[i]), "activate",
                      GTK_SIGNAL_FUNC (filter_field_select),(gpointer)i);
    }
    gtk_option_menu_set_menu (GTK_OPTION_MENU (g_string_menu), g_string_options);
    gtk_option_menu_set_menu (GTK_OPTION_MENU (g_numeric_menu), g_numeric_options);

    filter_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_container_set_border_width (GTK_CONTAINER (filter_window), 5);
    gtk_window_set_title (GTK_WINDOW (filter_window), ("Filter Hits"));
    gtk_window_set_policy (GTK_WINDOW (filter_window), FALSE, FALSE, TRUE);
    gtk_signal_connect (GTK_OBJECT (filter_window), "destroy",
		      GTK_SIGNAL_FUNC (close_filter_window), NULL);

    vbox = gtk_vbox_new (FALSE, 10);
    gtk_widget_show (vbox);
    gtk_container_add (GTK_CONTAINER (filter_window), vbox);


    /**************** string row ****************************/

    hbox1 = gtk_hbox_new (FALSE, 10);
    gtk_widget_show (hbox1);
    gtk_container_add (GTK_CONTAINER (vbox), hbox1);

    g_string_radio = gtk_radio_button_new(NULL);
    gtk_box_pack_start(GTK_BOX (hbox1), g_string_radio,FALSE,FALSE,0);
    gtk_widget_show(g_string_radio);

    string_filter_label = gtk_label_new ("String:");
    gtk_widget_show (string_filter_label);
    gtk_label_set_justify (GTK_LABEL (string_filter_label), GTK_JUSTIFY_LEFT);
    gtk_box_pack_start(GTK_BOX (hbox1), string_filter_label,FALSE,FALSE,0);

    gtk_box_pack_start(GTK_BOX (hbox1), g_string_menu,FALSE,FALSE,0);

    g_string_filter = gtk_entry_new ();
    gtk_widget_set_usize(g_string_filter,150, 20 ); 
    gtk_widget_show (g_string_filter);
    gtk_box_pack_start(GTK_BOX (hbox1), g_string_filter,FALSE,FALSE,0);


    /************* numeric row *******************/


    hbox2 = gtk_hbox_new (FALSE, 10);
    gtk_widget_show (hbox2);
    gtk_container_add (GTK_CONTAINER (vbox), hbox2);

    g_numeric_radio = gtk_radio_button_new_from_widget((GtkRadioButton*)g_string_radio);
    gtk_box_pack_start(GTK_BOX (hbox2), g_numeric_radio,FALSE,FALSE,0);
    gtk_widget_show(g_numeric_radio);

    numeric_filter_label = gtk_label_new ("Numeric:");
    gtk_widget_show (numeric_filter_label);
    gtk_box_pack_start(GTK_BOX (hbox2), numeric_filter_label,FALSE,FALSE,0);
    gtk_label_set_justify (GTK_LABEL (numeric_filter_label), GTK_JUSTIFY_LEFT);

    gtk_box_pack_start(GTK_BOX (hbox2), g_numeric_menu,FALSE,FALSE,0);
    gtk_box_pack_start(GTK_BOX (hbox2), g_cmp_menu,FALSE,FALSE,0);

    g_numeric_filter = gtk_entry_new ();
    gtk_widget_set_usize(g_numeric_filter,120, 20 ); 
    gtk_widget_show (g_numeric_filter);
    gtk_box_pack_start(GTK_BOX (hbox2), g_numeric_filter,FALSE,FALSE,0);

    /***************** min ctg hits ****************************/    


    hbox5 = gtk_hbox_new (FALSE, 10);
    gtk_widget_show (hbox5);
    gtk_container_add (GTK_CONTAINER (vbox), hbox5);

    g_minctghits_radio = gtk_radio_button_new_from_widget((GtkRadioButton*)g_string_radio);
    gtk_box_pack_start(GTK_BOX (hbox5), g_minctghits_radio,FALSE,FALSE,0);
    gtk_widget_show(g_minctghits_radio);

    minctghits_filter_label = gtk_label_new ("Min hits per ctg:");
    gtk_widget_show (minctghits_filter_label);
    gtk_box_pack_start(GTK_BOX (hbox5), minctghits_filter_label,FALSE,FALSE,0);
    gtk_label_set_justify (GTK_LABEL (minctghits_filter_label), GTK_JUSTIFY_LEFT);

    g_minctghits_filter = gtk_entry_new ();
    gtk_widget_set_usize(g_minctghits_filter,50, 20 ); 
    gtk_widget_show (g_minctghits_filter);
    gtk_box_pack_start(GTK_BOX (hbox5), g_minctghits_filter,FALSE,FALSE,0);

    /****************** max ctgs hit ************************************/

    hbox6 = gtk_hbox_new (FALSE, 10);
    gtk_widget_show (hbox6);
    gtk_container_add (GTK_CONTAINER (vbox), hbox6);

    g_maxctgshit_radio = gtk_radio_button_new_from_widget((GtkRadioButton*)g_string_radio);
    gtk_box_pack_start(GTK_BOX (hbox6), g_maxctgshit_radio,FALSE,FALSE,0);
    gtk_widget_show(g_maxctgshit_radio);

    maxctgshit_filter_label = gtk_label_new ("Max ctgs hit:");
    gtk_widget_show (maxctgshit_filter_label);
    gtk_box_pack_start(GTK_BOX (hbox6), maxctgshit_filter_label,FALSE,FALSE,0);
    gtk_label_set_justify (GTK_LABEL (maxctgshit_filter_label), GTK_JUSTIFY_LEFT);

    g_maxctgshit_filter = gtk_entry_new ();
    gtk_widget_set_usize(g_maxctgshit_filter,50, 20 ); 
    gtk_widget_show (g_maxctgshit_filter);
    gtk_box_pack_start(GTK_BOX (hbox6), g_maxctgshit_filter,FALSE,FALSE,0);   

    /****************** max total hits ************************************/

    hbox7 = gtk_hbox_new (FALSE, 10);
    gtk_widget_show (hbox7);
    gtk_container_add (GTK_CONTAINER (vbox), hbox7);

    g_maxhits_radio = gtk_radio_button_new_from_widget((GtkRadioButton*)g_string_radio);
    gtk_box_pack_start(GTK_BOX (hbox7), g_maxhits_radio,FALSE,FALSE,0);
    gtk_widget_show(g_maxhits_radio); 

    maxhits_filter_label = gtk_label_new ("Max total hits:");
    gtk_widget_show (maxhits_filter_label);
    gtk_box_pack_start(GTK_BOX (hbox7), maxhits_filter_label,FALSE,FALSE,0);
    gtk_label_set_justify (GTK_LABEL (maxhits_filter_label), GTK_JUSTIFY_LEFT);

    g_maxhits_filter = gtk_entry_new ();
    gtk_widget_set_usize(g_maxhits_filter,50, 20 ); 
    gtk_widget_show (g_maxhits_filter);
    gtk_box_pack_start(GTK_BOX (hbox7), g_maxhits_filter,FALSE,FALSE,0); 

    /****************** ctg ends ************************************/

    hbox8 = gtk_hbox_new (FALSE, 10);
    gtk_widget_show (hbox8);
    gtk_container_add (GTK_CONTAINER (vbox), hbox8);

    g_ctgends_radio = gtk_radio_button_new_from_widget((GtkRadioButton*)g_string_radio);
    gtk_box_pack_start(GTK_BOX (hbox8), g_ctgends_radio,FALSE,FALSE,0);
    gtk_widget_show(g_ctgends_radio); 

    ctgends_filter_label = gtk_label_new ("Ctg ends only");
    gtk_widget_show (ctgends_filter_label);
    gtk_box_pack_start(GTK_BOX (hbox8), ctgends_filter_label,FALSE,FALSE,0);
    gtk_label_set_justify (GTK_LABEL (ctgends_filter_label), GTK_JUSTIFY_LEFT);

    fromend_filter_label = gtk_label_new ("   FromEnd:");
    gtk_widget_show (fromend_filter_label);
    gtk_box_pack_start(GTK_BOX (hbox8), fromend_filter_label,FALSE,FALSE,0);
    gtk_label_set_justify (GTK_LABEL (fromend_filter_label), GTK_JUSTIFY_LEFT);

    g_fromend_filter = gtk_entry_new ();
    gtk_widget_set_usize(g_fromend_filter,50, 20 ); 
    gtk_widget_show (g_fromend_filter);
    gtk_box_pack_start(GTK_BOX (hbox8), g_fromend_filter,FALSE,FALSE,0);    
    gtk_entry_set_text(GTK_ENTRY(g_fromend_filter),g_strdup_printf("%d",Pz.fromendInt)); 

    /*****************  buttons ********************************/    

    hbox3 = gtk_hbox_new (FALSE, 10);
    gtk_widget_show (hbox3);
    gtk_container_add (GTK_CONTAINER (vbox), hbox3);


    apply_btn = gtk_button_new_with_label(" Apply Filter ");
    //gtk_widget_set_usize(apply_btn,100 , 20); 
    gtk_widget_show(apply_btn);
    gtk_box_pack_start(GTK_BOX(hbox3), apply_btn, FALSE, FALSE, 0);
    gtk_signal_connect (GTK_OBJECT (apply_btn), "clicked",
		 GTK_SIGNAL_FUNC (apply_btn_callback), NULL);

    undo_btn = gtk_button_new_with_label(" Undo Last Filter ");
    //gtk_widget_set_usize(undo_btn,100 , 20); 
    gtk_widget_show(undo_btn);
    gtk_box_pack_start(GTK_BOX(hbox3), undo_btn, FALSE, FALSE, 0);
    gtk_signal_connect (GTK_OBJECT (undo_btn), "clicked",
		 GTK_SIGNAL_FUNC (undo_btn_callback), NULL);

    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g_numeric_radio), TRUE);
    gtk_menu_item_activate((GtkMenuItem*)gtk_menu_get_active((GtkMenu*)g_numeric_options));


    /*************** history ************************************************/

    fpc_history_list = (GtkCList*)gtk_clist_new_with_titles(3, (gchar**) history_titles );
    gtk_clist_column_titles_passive( (GtkCList*) fpc_history_list );
    gtk_clist_set_column_width( (GtkCList*)fpc_history_list,0,100 );
    gtk_clist_set_column_width( (GtkCList*)fpc_history_list,1,100 );
    gtk_clist_set_column_width( (GtkCList*)fpc_history_list,2,100 );

    gtk_widget_show((GtkWidget*)fpc_history_list);

    gtk_box_pack_start(GTK_BOX(vbox),(GtkWidget*) fpc_history_list, FALSE, FALSE, 0);

    init_fpc_history_list();

    gtk_widget_show(filter_window);
    
}
int bss_is_rc(const BSS_HIT* hit)
{
    return (hit->data[BSS_RC].str[0] == 'y' ? 1 : 0);
}


